﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Mime;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Windows.Media;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using System.Xml.Serialization;
using Microsoft.Samples.CustomControls;

namespace FontDialogSample
{
    public class Font : IXmlSerializable, IEquatable<Font>
    {
        public Font()
        {}

        public Font(Brush foreground, FontFamily fontFamily, double fontSize, FontStretch fontStretch,
            FontStyle fontStyle, FontWeight fontWeight, TextDecorationCollection textDecorations)
        {
            Foreground = foreground;
            FontFamily = fontFamily;
            FontSize = fontSize;
            FontStretch = fontStretch;
            FontStyle = fontStyle;
            FontWeight = fontWeight;
            TextDecorations = textDecorations;
        }

        public TextDecorationCollection TextDecorations { get; set; }
        public FontFamily FontFamily { get; set; }
        public Brush Foreground { get; set; }
        public double FontSize { get; set; }
        public FontStretch FontStretch { get; set; }
        public FontStyle FontStyle { get; set; }
        public FontWeight FontWeight { get; set; }


        public static void ApplyFont(FontChooser from, dynamic to)
        {
            ApplyFont(to, from.SelectedFontColor, from.SelectedFontFamily, from.SelectedFontSize,
                from.SelectedFontStretch, from.SelectedFontStyle, from.SelectedFontWeight,
                from.SelectedTextDecorations);
        }

        public static void ApplyFont(FontChooser from, FontChooser to)
        {
            ApplyFont(to, from.SelectedFontColor, from.SelectedFontFamily, from.SelectedFontSize,
                from.SelectedFontStretch, from.SelectedFontStyle, from.SelectedFontWeight,
                from.SelectedTextDecorations);
        }

        public static void ApplyFont(dynamic to, Brush selectedFontColor, FontFamily selectedFontFamily, double selectedFontSize,
            FontStretch selectedFontStretch, FontStyle selectedFontStyle, FontWeight selectedFontWeight,
            TextDecorationCollection selectedTextDecorations)
        {
            to.Foreground = selectedFontColor;
            to.FontFamily = selectedFontFamily;
            to.FontSize = selectedFontSize;
            to.FontStretch = selectedFontStretch;
            to.FontStyle = selectedFontStyle;
            to.FontWeight = selectedFontWeight;

            to.TextDecorations = to.TextDecorations.Clone();
            to.TextDecorations.Clear();
            if (selectedTextDecorations != null)
            {
                foreach (TextDecoration decoration in selectedTextDecorations)
                {
                    to.TextDecorations.Add(decoration);
                }
            }
        }

        public static void ApplyFont(dynamic to, Font font)
        {
            ApplyFont(to, font.Foreground, font.FontFamily, font.FontSize, font.FontStretch,
                      font.FontStyle, font.FontWeight, font.TextDecorations);
        }

        public static Font ExtractFont(FontChooser from)
        {
            var font = from.SelectedFont;
            return font;
        }

        public static Font ExtractFont(TextBox from)
        {
            var font = new Font
                           {
                               FontFamily = from.FontFamily, 
                               FontSize = from.FontSize,
                               FontStretch = from.FontStretch,
                               FontStyle = from.FontStyle,
                               FontWeight = from.FontWeight,
                               Foreground = from.Foreground,
                               TextDecorations = from.TextDecorations
                           };
            return font;
        }

        public static Font ExtractFont(dynamic from)
        {
            var font = new Font
                           {
                               FontFamily = from.FontFamily, 
                               FontSize = from.FontSize,
                               FontStretch = from.FontStretch,
                               FontStyle = from.FontStyle,
                               FontWeight = from.FontWeight,
                               Foreground = from.Foreground,
                               TextDecorations = from.TextDecorations
                           };
            return font;
        }

        public static void ApplyFont(FontChooser to, Brush selectedFontColor, FontFamily selectedFontFamily, double selectedFontSize,
            FontStretch selectedFontStretch, FontStyle selectedFontStyle, FontWeight selectedFontWeight,
            TextDecorationCollection selectedTextDecorations)
        {
            to.SelectedTextDecorations = to.SelectedTextDecorations.Clone();
            to.SelectedTextDecorations.Clear();
            if (selectedTextDecorations != null)
            {
                foreach (TextDecoration decoration in selectedTextDecorations)
                {
                    to.SelectedTextDecorations.Add(decoration);
                }
            }

            to.SelectedFontColor = (SolidColorBrush)selectedFontColor;
            to.SelectedFontFamily = selectedFontFamily;
            to.SelectedFontSize = selectedFontSize;
            to.SelectedFontStretch = selectedFontStretch;
            to.SelectedFontStyle = selectedFontStyle;
            to.SelectedFontWeight = selectedFontWeight;
            to.ScheduleUpdate();
        }

        public static void Bind(TextElement fontHandler, FontChooser to)
        {
            BindProperty(fontHandler, TextElement.ForegroundProperty, to, "SelectedFontColor");
            BindProperty(fontHandler, TextElement.FontFamilyProperty, to, "SelectedFontFamily");
            BindProperty(fontHandler, TextElement.FontSizeProperty, to, "SelectedFontSize");
            BindProperty(fontHandler, TextElement.FontStretchProperty, to, "SelectedFontStretch");
            BindProperty(fontHandler, TextElement.FontStyleProperty, to, "SelectedFontStyle");
            BindProperty(fontHandler, TextElement.FontWeightProperty, to, "SelectedFontWeight");

            var decorationsField = fontHandler.GetType().GetField("TextDecorationsProperty");
            if (decorationsField != null)
            {
                var dproperty = (DependencyProperty)decorationsField.GetValue(null);
                BindProperty(fontHandler, dproperty, to, "SelectedTextDecorations");
            }

        }

        private static void BindProperty(dynamic obj, DependencyProperty property, FontChooser source, string path)
        {
            Binding binding = new Binding(path);
            binding.Source = source;
            obj.SetBinding(property, binding);
        }

        #region Implementation of IXmlSerializable

        /// <summary>
        /// This method is reserved and should not be used. When implementing the IXmlSerializable interface, you should return null (Nothing in Visual Basic) from this method, and instead, if specifying a custom schema is required, apply the <see cref="T:System.Xml.Serialization.XmlSchemaProviderAttribute"/> to the class.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Xml.Schema.XmlSchema"/> that describes the XML representation of the object that is produced by the <see cref="M:System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)"/> method and consumed by the <see cref="M:System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)"/> method.
        /// </returns>
        public XmlSchema GetSchema()
        {
            return null;
        }

        /// <summary>
        /// Generates an object from its XML representation.
        /// </summary>
        /// <param name="reader">The <see cref="T:System.Xml.XmlReader"/> stream from which the object is deserialized. </param>
        public void ReadXml(XmlReader reader)
        {
            XDocument xdoc = XDocument.Load(reader);

            var root = xdoc.Root;

            FontFamily = (FontFamily)XamlReader.Parse(root.Element("FontFamily").Value);
            FontSize = double.Parse(root.Element("FontSize").Value);
            FontStretch = (FontStretch)XamlReader.Parse(root.Element("FontStretch").Value);
            FontStyle = (FontStyle)XamlReader.Parse(root.Element("FontStyle").Value);
            FontWeight = (FontWeight)XamlReader.Parse(root.Element("FontWeight").Value);
            TextDecorations = (TextDecorationCollection)XamlReader.Parse(root.Element("TextDecorations").Value);
            Foreground = (Brush)XamlReader.Parse(root.Element("Foreground").Value);
        }

        /// <summary>
        /// Converts an object into its XML representation.
        /// </summary>
        /// <param name="writer">The <see cref="T:System.Xml.XmlWriter"/> stream to which the object is serialized. </param>
        public void WriteXml(XmlWriter writer)
        {
            writer.WriteElementString("FontFamily", XamlWriter.Save(FontFamily));
            writer.WriteElementString("FontSize", FontSize.ToString());
            writer.WriteElementString("FontStretch", XamlWriter.Save(FontStretch));
            writer.WriteElementString("FontStyle", XamlWriter.Save(FontStyle));
            writer.WriteElementString("FontWeight", XamlWriter.Save(FontWeight));
            writer.WriteElementString("TextDecorations", XamlWriter.Save(TextDecorations));
            writer.WriteElementString("Foreground", XamlWriter.Save(Foreground));
        }

        #endregion

        public static bool operator ==(Font font1, Font font2)
        {
            if (Equals(font1, null) && Equals(font2, null)) return true;
            if (!Equals(font1, null) && !Equals(font2, null)) return font1.Equals(font2);
            return false;
        }

        public static bool operator !=(Font font1, Font font2)
        {
            return !(font1 == font2);
        }

        public override bool Equals(object obj)
        {
            if (!(obj is Font)) return false;
            return this.Equals(obj as Font);
        }

        public override int GetHashCode()
        {
            return new object[]
                   {
                       FontFamily, 
                       FontSize, 
                       FontStretch, 
                       FontStyle, 
                       FontWeight, 
                       Foreground, 
                       TextDecorations
                   }
                .Select(x => x.GetHashCode())
                .Aggregate((x, y) => x ^ y);
        }

        public bool Equals(Font other)
        {
            if (other == null) return false;
            return this.FontFamily == other.FontFamily
                   && this.FontSize == other.FontSize
                   && this.FontStretch == other.FontStretch
                   && this.FontStyle == other.FontStyle
                   && this.FontWeight == other.FontWeight
                   && this.Foreground == other.Foreground
                   && this.TextDecorations == other.TextDecorations;
        }

        public override string ToString()
        {
            return string.Join(Environment.NewLine,
                "Font family: " + FontFamily,
                "Size: " + FontSize);
        }
    }
}
